local sqrt = math.sqrt
local log = math.log
local abs = math.abs

local Bezier = class("Bezier")

--长度函数反函数计算终止条件
local STOP_VALUE = 0.000001
local CACHE_PRECISION = 60

--[[
	构造二次贝塞尔曲线
	@param points 三个控制点
]]
function Bezier:ctor(points, timeLen, toCache)
	self._points = points
	self._timeLen = timeLen

	if toCache then
		self._cache = {}
	end

	if points[1].x == points[2].x and points[2].x == points[3].x then
		points[3].x = points[3].x + 0.1
	end

	if points[1].y == points[2].y and points[2].y == points[3].y then
		points[3].y = points[3].y + 0.1
	end

	local ax = points[1].x - 2 * points[2].x + points[3].x
	local ay = points[1].y - 2 * points[2].y + points[3].y
	local bx = 2 * points[2].x - 2 * points[1].x
	local by = 2 * points[2].y - 2 * points[1].y
	self._a = 4 * (ax * ax + ay * ay)
	self._b = 4 * (ax * bx + ay * by)
	self._c = bx * bx + by * by

	--曲线总长度
	self._totalLength = self:_length(1)
end

function Bezier:getTimeLen()
	return self._timeLen
end

--[[
	返回【匀速运动】情况下指定时间点坐标
	@param t 范围0~1
	@result 对应时间点的坐标
]]
function Bezier:getPoint(t, fromCache)
	local timeIndex
	if self._cache and fromCache then
		timeIndex = math.round(t * CACHE_PRECISION)
		local p02X, p02Y = self._cache[4 * timeIndex - 3], self._cache[4 * timeIndex - 2]
		local p01X, p01Y = self._cache[4 * timeIndex - 1], self._cache[4 * timeIndex]
		if p02X then
			return {x = p02X, y = p02Y}, {x = p01X, y = p01Y}
		end
	end

	t = t / self._timeLen

	--如果按照线性增长,此时对应的曲线长度
	local l = t * self._totalLength

	--根据L函数的反函数，求得l对应的t值
	t = self:_invertL(t, l)

	--根据贝塞尔曲线函数，求得取得此时的x,y坐标
	local p01 = {x = (1 - t) * self._points[1].x + t * self._points[2].x
		, y = (1 - t) * self._points[1].y + t * self._points[2].y}
	local p11 = {x = (1 - t) * self._points[2].x + t * self._points[3].x
		, y = (1 - t) * self._points[2].y + t * self._points[3].y}
	local p02 = {x = (1 - t) * p01.x + t * p11.x
		, y = (1 - t) * p01.y + t * p11.y}

	if self._cache and fromCache then
		self._cache[4 * timeIndex - 3] = p02.x
		self._cache[4 * timeIndex - 2] = p02.y
		self._cache[4 * timeIndex - 1] = p01.x
		self._cache[4 * timeIndex] = p01.y
	end

	return p02, p01, p11
end

--[[
	速度函数
	s(t_) = Sqrt[A*t*t+B*t+C]
]]
function Bezier:_speed(t)
	return sqrt(self._a * t * t + self._b * t + self._c);
end

--[[长度函数
L(t) = Integrate[s[t], t]

L(t_) = ((2*Sqrt[A]*(2*A*t*Sqrt[C + t*(B + A*t)] + B*(-Sqrt[C] + Sqrt[C + t*(B + A*t)])) + 
			(B^2 - 4*A*C) (Log[B + 2*Sqrt[A]*Sqrt[C] ] - Log[B + 2*A*t + 2 Sqrt[A]*Sqrt[C + t*(B + A*t)] ]))
				/(8* A^(3/2)));

]]
function Bezier:_length(t)
	local temp1 = sqrt(self._c + t * (self._b + self._a * t))
	local temp2 = (2 * self._a * t * temp1 + self._b * (temp1 - sqrt(self._c)))
	local temp3 = log(self._b + 2 * sqrt(self._a) * sqrt(self._c))
	local temp4 = log(self._b + 2 * self._a * t + 2 * sqrt(self._a) * temp1)
	local temp5 = 2* sqrt(self._a) * temp2
	local temp6 = (self._b* self._b - 4 * self._a * self._c) * (temp3 - temp4)

	return (temp5 + temp6)/(8 * (self._a ^ 1.5))
end

--[[
	长度函数反函数，使用牛顿切线法求解
	X(n+1) = Xn - F(Xn)/F'(Xn)
]]
function Bezier:_invertL(t, l)
	local t1 = t
	local t2
	while true do
		t2 = t1 - (self:_length(t1) - l) / self:_speed(t1)
		if abs(t1 - t2) < STOP_VALUE then 
			break
		end
		t1 = t2
	end

	return t2
end

return Bezier